/*! @file protocol.c
	\brief Functions for getting and giving information via tcp/ip conenction
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

#ifdef DEBUG
# include <assert.h>
#endif

#include <gr_graph_fun.h>

#include "w3ld.h"
#include "server_protocol.h"
#include "perror_exit.h"
#include "func_lexer.h"

extern char *get_xml_text_graph(GraphFrame *gf,long *len,int type);

extern cmd_ret *w3l_disconnect();

/*!
 * \brief Read a request
 * @param sd a live socket descriptor
 * @return The request string
 */
char *request_read(int sd) {
	/*! @var rqst
		\brief The request string */
	char *rqst = NULL;
	long len = 0;
	int rec,tmp;

	assert(sd!=0);
	
	// Read the length byte
	if(recv(sd,&len,sizeof(long),0)!=sizeof(long)) {
		perror("recv()");
		pthread_exit(NULL);
	}
	assert(len>=0);

	/*
	 *	We allocate a number of bytes directly taken from a client.
	 *	It is thus wise to do basic checking on the size
	 */
	if(len > 0x00ff) {
#ifdef DEBUG
		fprintf(stderr,"debug: killing user for having demanded a huge amount of memory (%d bytes)\n",(int)len);
#endif
		w3l_disconnect();
	}

	MALLOCATE(rqst,char *,(int)(sizeof(char)*(len+1)));

	// Read the request
	rec = 0;
	while(rec < len) {
		if((tmp=recv(sd,rqst,(len-rec),0))<0) {
			perror("recv()");
			pthread_exit(NULL);
		}
		rec+=tmp;
	}
	assert(rqst!=NULL);
	rqst[len] = '\0';

#ifdef DEBUG
	fprintf(stderr,"debug: client requested %s\n",rqst);
#endif
	return rqst;
}


/*!
 * \brief Send a reply
 * @param sd a live socket descriptor
 * @param dtype data type of data being sent
 * @param data data, of dtype type being sent
 * @return void
 */
void reply_send(int sd, char dtype, void *data) {
	
	long len = 0;
	char *reply = NULL;
	char *msg = NULL;
	short free_reply = 0;
	long sent,tmp;
	LNode_type *ptr;
	cmd_ret *li;

	assert(!dtype || data!=NULL);

	switch(dtype) {
	case DATA_TYPE_ERROR:
	case DATA_TYPE_STRING:
		reply = (char *)data;
		len = strlen(reply);
		break;
	case DATA_TYPE_INT:
		if(!(reply = (char *)malloc(sizeof(char)*12)))
			perror_exit("%s %d",__FILE__,__LINE__);
		snprintf(reply,12,"%d",*((int *)data));
		free_reply = 1;
		len = strlen(reply);
		break;
	case DATA_TYPE_LIST:
		for(ptr = ((LNode_type *)data)->back ; ptr != (LNode_type *)data ; ptr = ptr->back) {
			li = (cmd_ret *)ptr->info;
			assert(li != NULL);

			REMALLOCATE(reply,char *,sizeof(char)*(len + sizeof(long) + sizeof(char) + li->data_length))

			memcpy(&(reply[len]),&(li->data_length),sizeof(long));
			memcpy(&(reply[len + sizeof(long)]),&(li->data_type),sizeof(char));
			memcpy(&(reply[len + sizeof(long) + sizeof(char)]),li->data,li->data_length);

			len += sizeof(long) + sizeof(char) + li->data_length;

			destroy_return(li);
		}
		free_reply = 1;
		break;
	case DATA_TYPE_GRAPH:
		assert(data != NULL);
		if(!(reply = get_xml_text_graph((GraphFrame *)data,&len,GR_STANDARD_FORMAT))) {
			reply = strdup("");
			len = 0;
		}
		free_reply = 1;
		len = strlen(reply);
		break;
	}

	MALLOCATE(msg,char *,sizeof(char)*((int)len+sizeof(long)+sizeof(char)))

	memcpy(&(msg[0]),&len,sizeof(long));                         // first four bytes are the message length
	memcpy(&(msg[sizeof(long)]),&dtype,sizeof(char));            // third byte is the data type
	memcpy(&(msg[sizeof(long)+sizeof(char)]),reply,len);         // the rest is the message (of length specified)

	sent = 0;
	while(sent < (len+sizeof(long)+sizeof(char))) {
		if((tmp=send(sd,msg,((len+sizeof(long)+sizeof(char))-sent),0))<0)
			perror("send()");
		sent+=tmp;
	}

	free(msg);

	if(free_reply)
		free(reply);

}
